package com.smedi.ismedi.progress.core.infrastructure.util.mpp;

import com.smedi.ismedi.authority.starter.exception.ErrorItem;
import com.smedi.ismedi.authority.starter.exception.LogicException;
import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.Relation;
import net.sf.mpxj.Resource;
import net.sf.mpxj.ResourceAssignment;
import net.sf.mpxj.Task;
import net.sf.mpxj.mpp.MPPReader;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author hexu@goldbeacon.cn
 * @version 1.0
 * @remark ...
 * @date 2022/10/9 16:05
 */
public class MppUtil {
    static final HashMap<String, String> DEPENDENCY_TYPES = new HashMap<>();

    public static void init() {
        DEPENDENCY_TYPES.put("FF", "3");
        DEPENDENCY_TYPES.put("FS", "2");
        DEPENDENCY_TYPES.put("SF", "1");
        DEPENDENCY_TYPES.put("SS", "0");
    }

    /**
     * 从MPP的Task读数据到数据库的BdTask
     *
     * @param task 任务信息
     * @return 封装结构
     */

    private static MppTask fetchTaskData(Task task) {
        MppTask gttTask = new MppTask();
        try {
            gttTask.setPriority(1);
            gttTask.setId(Long.valueOf(task.getUniqueID()));
            gttTask.setNumbers(task.getID());
            gttTask.setNamePrefix(task.getWBS());
            gttTask.setName(task.getName());
            double duration = task.getDuration().getDuration();
            gttTask.setDuration((int) duration);
            gttTask.setDurationUnit(task.getDuration().getUnits().toString());
            gttTask.setPercentDone((task.getPercentageComplete().doubleValue()));
            gttTask.setTaskLevel(task.getOutlineLevel());
            Calendar startDate = Calendar.getInstance();
            Calendar endDate = Calendar.getInstance();
            gttTask.setParentId(Long.valueOf(task.getParentTask().getUniqueID()));
            gttTask.setParentName(task.getParentTask().getName());
            //是否关键任务(是关键任务，并且是叶子节点)
            if (task.getCritical()  && CollectionUtils.isEmpty(task.getChildTasks())) {
                gttTask.setCritical(task.getCritical());
            }
            //是里程碑
            if (task.getMilestone()) {
                gttTask.setMilestone(true);
                startDate.setTime(task.getStart());
                endDate.setTime(task.getFinish());
            } else {
                gttTask.setMilestone(false);
                startDate.setTime(task.getStart());
                startDate.set(Calendar.HOUR_OF_DAY, 8);
                startDate.set(Calendar.MINUTE, 30);
                startDate.set(Calendar.SECOND, 0);
                endDate.setTime(task.getFinish());
                endDate.set(Calendar.HOUR_OF_DAY, 18);
                endDate.set(Calendar.MINUTE, 0);
                endDate.set(Calendar.SECOND, 0);
            }
            gttTask.setStartDate(LocalDateTime.ofInstant(startDate.toInstant(), ZoneId.systemDefault()));
            gttTask.setEndDate(LocalDateTime.ofInstant(endDate.toInstant(), ZoneId.systemDefault()));
            //读取资源名称
            List<ResourceAssignment> resourceAssignments =  task.getResourceAssignments();
            List<String> resourceNames = new ArrayList<>();
            for (ResourceAssignment resourceAssignment : resourceAssignments) {
                Resource resource = resourceAssignment.getResource();
                if(resource != null) {
                    resourceNames.add(resource.getName());
                }
            }
            gttTask.setResourceName(String.join(",", resourceNames));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return gttTask;
    }

    /**
     * 获取项目计划任务数据
     *
     * @param is MPP文件流
     * @param verificationPeerNameError 校验同级目录是否存在同任务名
     * @return 封装结果
     */
    public static List<MppTask> getTaskData(InputStream is, StringBuilder verificationPeerNameError) {
        //File file = new File("C:\\Users\\EDZ\\Desktop\\总体进度计划模板0420.mpp");

        ProjectFile projectFile;
        MPPReader mppRead = new MPPReader();
        try {
            projectFile = mppRead.read(is);
        } catch (Exception e) {
            e.printStackTrace();
            throw new LogicException(new ErrorItem(100, "文件格式不正确，请使用Microsoft Project的mpp文件！"));
        }
        List<MppTask> list = new ArrayList<>();
        for (Task tempTask : projectFile.getChildTasks()) {
            int idx = 0;
            for (Task task : tempTask.getChildTasks()) {
                //添加最上层任务节点, 第三个参数parentId=0
                MppTask gttTask = fetchTaskData(task);
                //设置节点在姊妹节点下的顺序
                gttTask.setIndexs(idx++);
                list.add(gttTask);
                //递归重新设置父任务为关键任务（读取到的关键任务标识会存在丢失父节点标识的情况）
                //setParentTaskCritical(task);
                //递归调用
                listHierarchy(verificationPeerNameError, task, list);
            }
        }
        return list;
    }

    /**
     * 设置父任务为关键任务
     * @param task 任务
     */
    private static void setParentTaskCritical(Task task) {
        for (Task child : task.getChildTasks()) {
            if (child.getCritical()) {
                task.setCritical(true);
            }
            setParentTaskCritical(child);
        }
    }


    private static void listHierarchy(StringBuilder error, Task task, List<MppTask> list) {
        String verificationRes = verificationPeerName(task.getChildTasks());
        if (!StringUtils.isEmpty(verificationRes)) {
            error.append(verificationRes);
        }
        //节点的先后顺序
        int idx = 0;
        for (Task child : task.getChildTasks()) {
            MppTask gttTask = fetchTaskData(child);

            //设置任务的先后顺序
            gttTask.setIndexs(idx++);
            //获取前置任务信息，如果不需要
            List<MppDependency> dbDependencies = getBdDependencyDate(child);
            gttTask.setMppDependencyList(dbDependencies);
            list.add(gttTask);
            listHierarchy(error, child, list);

        }
    }

    /**
     * 校验mpp文件的必填字段是否为空（任务名称、开始时间、结束时间、工期）
     * @return 返回错误信息，通过则返回空
     */
    public static String verificationTaskRequiredField(List<MppTask> tasks) {
        StringBuilder errorInfo = new StringBuilder();
        for (MppTask dbTask: tasks) {
            StringBuilder sb = new StringBuilder();
            if (StringUtils.isEmpty(dbTask.getName())) {
                sb.append("[任务名称]");
            }
            if (dbTask.getStartDate() == null || dbTask.getEndDate() == null) {
                sb.append("[开始或完成时间]");
            }
            if (dbTask.getDuration() == null) {
                sb.append("[工期]");
            }
            if (sb.length() > 0) {
                errorInfo.append("行：").append(dbTask.getNumbers()).append(sb).append("不能为空，请检查项目文件！\n");
            }
        }
        if (errorInfo.length() > 0) {
            return errorInfo.toString();
        } else {
            return null;
        }
    }

    /**
     * 获取项目计划任务关联数据
     *
     * @param task MPP文件任务
     * @return 返回关联信息
     */

    public static List<MppDependency> getBdDependencyDate(Task task) {
        List<MppDependency> list = new ArrayList<>();
        //初始化MPP任务关联对应Gantt图的编号
        MppUtil.init();
        //节点之间的关系
        List<Relation> predecessors = task.getPredecessors();
        if (predecessors != null && !predecessors.isEmpty()) {
            for (Relation relation : predecessors) {
                MppDependency dep = new MppDependency();
                dep.setToId(relation.getSourceTask().getUniqueID().toString());
                dep.setFromId(relation.getTargetTask().getUniqueID().toString());
                dep.setLag(relation.getLag());
                dep.setType(getDependencyType(relation.getType().toString()));

                list.add(dep);
            }
        }
        return list;

    }

    static String getDependencyType(String content) {
        Object ret = DEPENDENCY_TYPES.get(content);
        return ret.toString();
    }


    /**
     * 校验mpp文件同级任务名称不能相同
     * @return 返回错误信息，通过则返回空
     */
    private static String verificationPeerName(List<Task> tasks) {
        Set<String> repeated = new HashSet<>();
        List<String> results = new ArrayList<>();
        for (Task task : tasks) {
            if (!repeated.add(task.getName())) {
                results.add(task.getName());
            }
        }
        if (!results.isEmpty()) {
            return results.toString();
        } else {
            return null;
        }
    }
}
